﻿@page "/"
@using System.ComponentModel
@using Microsoft.Agents.AI
@using Microsoft.Extensions.DependencyInjection
@* @inject IChatClient ChatClient *@
@inject NavigationManager Nav
@inject SemanticSearch Search
@inject IServiceProvider ServiceProvider
@implements IDisposable

<PageTitle>Chat</PageTitle>

<ChatHeader OnNewChat="@ResetConversationAsync" />

<ChatMessageList Messages="@messages" InProgressMessage="@currentResponseMessage">
	<NoMessagesContent>
		<div>To get started, try asking about these example documents. You can replace these with your own data and replace this message.</div>
		<ChatCitation File="Example_Emergency_Survival_Kit.pdf" />
		<ChatCitation File="Example_GPS_Watch.pdf" />
	</NoMessagesContent>
</ChatMessageList>

<div class="chat-container">
	<ChatSuggestions OnSelected="@AddUserMessageAsync" @ref="@chatSuggestions" />
	<ChatInput OnSend="@AddUserMessageAsync" @ref="@chatInput" />
	<SurveyPrompt /> @* Remove this line to eliminate the template survey message *@
</div>

@code {

	private int statefulMessageCount;
	private readonly ChatOptions chatOptions = new();
	private readonly List<ChatMessage> messages = new();
	private CancellationTokenSource? currentResponseCancellation;
	private ChatMessage? currentResponseMessage;
	private ChatInput? chatInput;
	private ChatSuggestions? chatSuggestions;

	// Resolve keyed AI agent named "CoordinatorAgent" from ServiceProvider in OnInitialized
	private AIAgent aiAgent = default!;

	protected override void OnInitialized()
	{
		// Resolve the keyed AI agent registered as "ChatAgent" in Program.cs
		aiAgent = ServiceProvider.GetRequiredKeyedService<AIAgent>("CoordinatorAgent");
		
		statefulMessageCount = 0;
		chatOptions.Tools = [AIFunctionFactory.Create(SearchAsync)];
	}

	private async Task AddUserMessageAsync(ChatMessage userMessage)
	{
		CancelAnyCurrentResponse();

		// Add the user message to the conversation
		messages.Add(userMessage);
		chatSuggestions?.Clear();
		await chatInput!.FocusAsync();

		// Stream and display a new response from the IChatClient
		var responseText = new TextContent("");
		currentResponseMessage = new ChatMessage(ChatRole.Assistant, [responseText]);
		currentResponseCancellation = new();
		// await foreach (var update in ChatClient.GetStreamingResponseAsync(messages.Skip(statefulMessageCount), chatOptions, currentResponseCancellation.Token))
		// {
		//     messages.AddMessages(update, filter: c => c is not TextContent);
		//     responseText.Text += update.Text;
		//     chatOptions.ConversationId = update.ConversationId;
		//     ChatMessageItem.NotifyChanged(currentResponseMessage);
		// }

		await foreach (var update in aiAgent.RunStreamingAsync(
			messages: messages.Skip(statefulMessageCount),
			cancellationToken: currentResponseCancellation.Token))
		{
			var responseUpdate = update.AsChatResponseUpdate();
			messages.AddMessages(responseUpdate, filter: c => c is not TextContent);
			responseText.Text += update.Text;
			chatOptions.ConversationId = responseUpdate.ConversationId;
			ChatMessageItem.NotifyChanged(currentResponseMessage);
		}

		// Store the final response in the conversation, and begin getting suggestions
		messages.Add(currentResponseMessage!);
		statefulMessageCount = chatOptions.ConversationId is not null ? messages.Count : 0;
		currentResponseMessage = null;
		chatSuggestions?.Update(messages);
	}

	private void CancelAnyCurrentResponse()
	{
		// If a response was cancelled while streaming, include it in the conversation so it's not lost
		if (currentResponseMessage is not null)
		{
			messages.Add(currentResponseMessage);
		}

		currentResponseCancellation?.Cancel();
		currentResponseMessage = null;
	}

	private async Task ResetConversationAsync()
	{
		CancelAnyCurrentResponse();
		messages.Clear();
		chatOptions.ConversationId = null;
		statefulMessageCount = 0;
		chatSuggestions?.Clear();
		await chatInput!.FocusAsync();
	}

	[Description("Searches for information using a phrase or keyword")]
	private async Task<IEnumerable<string>> SearchAsync(
		[Description("The phrase to search for.")] string searchPhrase,
		[Description("If possible, specify the filename to search that file only. If not provided or empty, the search includes all files.")] string? filenameFilter = null)
	{
		await InvokeAsync(StateHasChanged);
		var results = await Search.SearchAsync(searchPhrase, filenameFilter, maxResults: 5);
		return results.Select(result =>
			$"<result filename=\"{result.DocumentId}\" page_number=\"{result.PageNumber}\">{result.Text}</result>");
	}

	public void Dispose()
		=> currentResponseCancellation?.Cancel();
}
